home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 001-100 / 001-025 / 002 / microemacs / display.c < prev    next >
C/C++ Source or Header  |  1995-03-17  |  21KB  |  860 lines

  1. /*
  2.  * The functions in this file handle redisplay. There are two halves, the
  3.  * ones that update the virtual display screen, and the ones that make the
  4.  * physical display screen the same as the virtual display screen. These
  5.  * functions use hints that are left in the windows by the commands.
  6.  *
  7.  * REVISION HISTORY:
  8.  *
  9.  * ?    Steve Wilhite, 1-Dec-85
  10.  *      - massive cleanup on code.
  11.  */
  12.  
  13. #include        <stdio.h>
  14. #include        "ed.h"
  15.  
  16. #define WFDEBUG 0                       /* Window flag debug. */
  17.  
  18. typedef struct  VIDEO {
  19.         short   v_flag;                 /* Flags */
  20.         char    v_text[1];              /* Screen data. */
  21. }       VIDEO;
  22.  
  23. #define VFCHG   0x0001                  /* Changed. */
  24.  
  25. int     sgarbf  = TRUE;                 /* TRUE if screen is garbage */
  26. int     mpresf  = FALSE;                /* TRUE if message in last line */
  27. int     vtrow   = 0;                    /* Row location of SW cursor */
  28. int     vtcol   = 0;                    /* Column location of SW cursor */
  29. int     ttrow   = HUGE;                 /* Row location of HW cursor */
  30. int     ttcol   = HUGE;                 /* Column location of HW cursor */
  31.  
  32. VIDEO   **vscreen;                      /* Virtual screen. */
  33. VIDEO   **pscreen;                      /* Physical screen. */
  34.  
  35. /*
  36.  * Initialize the data structures used by the display code. The edge vectors
  37.  * used to access the screens are set up. The operating system's terminal I/O
  38.  * channel is set up. All the other things get initialized at compile time.
  39.  * The original window has "WFCHG" set, so that it will get completely
  40.  * redrawn on the first call to "update".
  41.  */
  42. vtinit()
  43. {
  44.     register int i;
  45.     register VIDEO *vp;
  46.  
  47.     (*term.t_open)();
  48.     vscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
  49.  
  50.     if (vscreen == NULL)
  51.         exit(1);
  52.  
  53.     pscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
  54.  
  55.     if (pscreen == NULL)
  56.         exit(1);
  57.  
  58.     for (i = 0; i < term.t_nrow; ++i)
  59.         {
  60.         vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  61.  
  62.         if (vp == NULL)
  63.             exit(1);
  64.  
  65.         vscreen[i] = vp;
  66.         vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  67.  
  68.         if (vp == NULL)
  69.             exit(1);
  70.  
  71.         pscreen[i] = vp;
  72.         }
  73. }
  74.  
  75. /*
  76.  * Clean up the virtual terminal system, in anticipation for a return to the
  77.  * operating system. Move down to the last line and clear it out (the next
  78.  * system prompt will be written in the line). Shut down the channel to the
  79.  * terminal.
  80.  */
  81. vttidy()
  82. {
  83.     movecursor(term.t_nrow, 0);
  84.     (*term.t_eeol)();
  85.     (*term.t_close)();
  86. }
  87.  
  88. /*
  89.  * Set the virtual cursor to the specified row and column on the virtual
  90.  * screen. There is no checking for nonsense values; this might be a good
  91.  * idea during the early stages.
  92.  */
  93. vtmove(row, col)
  94. {
  95.     vtrow = row;
  96.     vtcol = col;
  97. }
  98.  
  99. /*
  100.  * Write a character to the virtual screen. The virtual row and column are
  101.  * updated. If the line is too long put a "$" in the last column. This routine
  102.  * only puts printing characters into the virtual terminal buffers. Only
  103.  * column overflow is checked.
  104.  */
  105. vtputc(c)
  106.     int c;
  107. {
  108.     register VIDEO      *vp;
  109.  
  110.     vp = vscreen[vtrow];
  111.  
  112.     if (vtcol >= term.t_ncol)
  113.         vp->v_text[term.t_ncol - 1] = '$';
  114.     else if (c == '\t')
  115.         {
  116.         do
  117.             {
  118.             vtputc(' ');
  119.             }
  120.         while ((vtcol&0x07) != 0);
  121.         }
  122.     else if (c < 0x20 || c == 0x7F)
  123.         {
  124.         vtputc('^');
  125.         vtputc(c ^ 0x40);
  126.         }
  127.     else
  128.         vp->v_text[vtcol++] = c;                
  129. }
  130.  
  131. /*
  132.  * Erase from the end of the software cursor to the end of the line on which
  133.  * the software cursor is located.
  134.  */
  135. vteeol()
  136. {
  137.     register VIDEO      *vp;
  138.  
  139.     vp = vscreen[vtrow];
  140.     while (vtcol < term.t_ncol)
  141.         vp->v_text[vtcol++] = ' ';
  142. }
  143.  
  144. /*
  145.  * Make sure that the display is right. This is a three part process. First,
  146.  * scan through all of the windows looking for dirty ones. Check the framing,
  147.  * and refresh the screen. Second, make sure that "currow" and "curcol" are
  148.  * correct for the current window. Third, make the virtual and physical
  149.  * screens the same.
  150.  */
  151. update()
  152. {
  153.     register LINE *lp;
  154.     register WINDOW *wp;
  155.     register VIDEO *vp1;
  156.     register VIDEO *vp2;
  157.     register int i;
  158.     register int j;
  159.     register int c;
  160.  
  161.     wp = wheadp;
  162.  
  163.     while (wp != NULL)
  164.         {
  165.         /* Look at any window with update flags set on. */
  166.  
  167.         if (wp->w_flag != 0)
  168.             {
  169.             /* If not force reframe, check the framing. */
  170.  
  171.             if ((wp->w_flag & WFFORCE) == 0)
  172.                 {
  173.                 lp = wp->w_linep;
  174.  
  175.                 for (i = 0; i < wp->w_ntrows; ++i)
  176.                     {
  177.                     if (lp == wp->w_dotp)
  178.                         goto out;
  179.  
  180.                     if (lp == wp->w_bufp->b_linep)
  181.                         break;
  182.  
  183.                     lp = lforw(lp);
  184.                     }
  185.                 }
  186.  
  187.             /* Not acceptable, better compute a new value for the line at the
  188.              * top of the window. Then set the "WFHARD" flag to force full
  189.              * redraw.
  190.              */
  191.             i = wp->w_force;
  192.  
  193.             if (i > 0)
  194.                 {
  195.                 --i;
  196.  
  197.                 if (i >= wp->w_ntrows)
  198.                   i = wp->w_ntrows-1;
  199.                 }
  200.             else if (i < 0)
  201.                 {
  202.                 i += wp->w_ntrows;
  203.  
  204.                 if (i < 0)
  205.                     i = 0;
  206.                 }
  207.             else
  208.                 i = wp->w_ntrows/2;
  209.  
  210.             lp = wp->w_dotp;
  211.  
  212.             while (i != 0 && lback(lp) != wp->w_bufp->b_linep)
  213.                 {
  214.                 --i;
  215.                 lp = lback(lp);
  216.                 }
  217.  
  218.             wp->w_linep = lp;
  219.             wp->w_flag |= WFHARD;       /* Force full. */
  220.  
  221. out:
  222.             /* Try to use reduced update. Mode line update has its own special
  223.              * flag. The fast update is used if the only thing to do is within
  224.              * the line editing.
  225.              */
  226.             lp = wp->w_linep;
  227.             i = wp->w_toprow;
  228.  
  229.             if ((wp->w_flag & ~WFMODE) == WFEDIT)
  230.                 {
  231.                 while (lp != wp->w_dotp)
  232.                     {
  233.                     ++i;
  234.                     lp = lforw(lp);
  235.                     }
  236.  
  237.                 vscreen[i]->v_flag |= VFCHG;
  238.                 vtmove(i, 0);
  239.  
  240.                 for (j = 0; j < llength(lp); ++j)
  241.                     vtputc(lgetc(lp, j));
  242.  
  243.                 vteeol();
  244.                 }
  245.              else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0)
  246.                 {
  247.                 while (i < wp->w_toprow+wp->w_ntrows)
  248.                     {
  249.                     vscreen[i]->v_flag |= VFCHG;
  250.                     vtmove(i, 0);
  251.  
  252.                     if (lp != wp->w_bufp->b_linep)
  253.                         {
  254.                         for (j = 0; j < llength(lp); ++j)
  255.                             vtputc(lgetc(lp, j));
  256.  
  257.                         lp = lforw(lp);
  258.                         }
  259.  
  260.                     vteeol();
  261.                     ++i;
  262.                     }
  263.                 }
  264. #if ~WFDEBUG
  265.             if ((wp->w_flag&WFMODE) != 0)
  266.                 modeline(wp);
  267.  
  268.             wp->w_flag  = 0;
  269.             wp->w_force = 0;
  270. #endif
  271.             }           
  272. #if WFDEBUG
  273.         modeline(wp);
  274.         wp->w_flag =  0;
  275.         wp->w_force = 0;
  276. #endif
  277.         wp = wp->w_wndp;
  278.         }
  279.  
  280.     /* Always recompute the row and column number of the hardware cursor. This
  281.      * is the only update for simple moves.
  282.      */
  283.     lp = curwp->w_linep;
  284.     currow = curwp->w_toprow;
  285.  
  286.     while (lp != curwp->w_dotp)
  287.         {
  288.         ++currow;
  289.         lp = lforw(lp);
  290.         }
  291.  
  292.     curcol = 0;
  293.     i = 0;
  294.  
  295.     while (i < curwp->w_doto)
  296.         {
  297.         c = lgetc(lp, i++);
  298.  
  299.         if (c == '\t')
  300.             curcol |= 0x07;
  301.         else if (c < 0x20 || c == 0x7F)
  302.             ++curcol;
  303.  
  304.         ++curcol;
  305.         }
  306.  
  307.     if (curcol >= term.t_ncol)          /* Long line. */
  308.         curcol = term.t_ncol-1;
  309.  
  310.     /* Special hacking if the screen is garbage. Clear the hardware screen,
  311.      * and update your copy to agree with it. Set all the virtual screen
  312.      * change bits, to force a full update.
  313.      */
  314.     if (sgarbf != FALSE)
  315.         {
  316.         for (i = 0; i < term.t_nrow; ++i)
  317.             {
  318.             vscreen[i]->v_flag |= VFCHG;
  319.             vp1 = pscreen[i];
  320.             for (j = 0; j < term.t_ncol; ++j)
  321.                 vp1->v_text[j] = ' ';
  322.             }
  323.  
  324.         movecursor(0, 0);               /* Erase the screen. */
  325.         (*term.t_eeop)();
  326.         sgarbf = FALSE;                 /* Erase-page clears */
  327.         mpresf = FALSE;                 /* the message area. */
  328.         }
  329.  
  330.     /* Make sure that the physical and virtual displays agree. Unlike before,
  331.      * the "updateline" code is only called with a line that has been updated
  332.      * for sure.
  333.      */
  334.     for (i = 0; i < term.t_nrow; ++i)
  335.         {
  336.         vp1 = vscreen[i];
  337.  
  338.         if ((vp1->v_flag&VFCHG) != 0)
  339.             {
  340.             vp1->v_flag &= ~VFCHG;
  341.             vp2 = pscreen[i];
  342.             updateline(i, &vp1->v_text[0], &vp2->v_text[0]);
  343.             }
  344.         }
  345.  
  346.     /* Finally, update the hardware cursor and flush out buffers. */
  347.  
  348.     movecursor(currow, curcol);
  349.     (*term.t_flush)();
  350. }
  351.  
  352. /*
  353.  * Update a single line. This does not know how to use insert or delete
  354.  * character sequences; we are using VT52 functionality. Update the physical
  355.  * row and column variables. It does try an exploit erase to end of line. The
  356.  * RAINBOW version of this routine uses fast video.
  357.  */
  358. updateline(row, vline, pline)
  359.     char vline[];
  360.     char pline[];
  361. {
  362. #if RAINBOW
  363.     register char *cp1;
  364.     register char *cp2;
  365.     register int nch;
  366.  
  367.     cp1 = &vline[0];                    /* Use fast video. */
  368.     cp2 = &pline[0];
  369.     putline(row+1, 1, cp1);
  370.     nch = term.t_ncol;
  371.  
  372.     do
  373.         {
  374.         *cp2 = *cp1;
  375.         ++cp2;
  376.         ++cp1;
  377.         }
  378.     while (--nch);
  379. #else
  380.     register char *cp1;
  381.     register char *cp2;
  382.     register char *cp3;
  383.     register char *cp4;
  384.     register char *cp5;
  385.     register int nbflag;
  386.  
  387.     cp1 = &vline[0];                    /* Compute left match.  */
  388.     cp2 = &pline[0];
  389.  
  390.     while (cp1!=&vline[term.t_ncol] && cp1[0]==cp2[0])
  391.         {
  392.         ++cp1;
  393.         ++cp2;
  394.         }
  395.  
  396.     /* This can still happen, even though we only call this routine on changed
  397.      * lines. A hard update is always done when a line splits, a massive
  398.      * change is done, or a buffer is displayed twice. This optimizes out most
  399.      * of the excess updating. A lot of computes are used, but these tend to
  400.      * be hard operations that do a lot of update, so I don't really care.
  401.      */
  402.     if (cp1 == &vline[term.t_ncol])             /* All equal. */
  403.         return;
  404.  
  405.     nbflag = FALSE;
  406.     cp3 = &vline[term.t_ncol];          /* Compute right match. */
  407.     cp4 = &pline[term.t_ncol];
  408.  
  409.     while (cp3[-1] == cp4[-1])
  410.         {
  411.         --cp3;
  412.         --cp4;
  413.         if (cp3[0] != ' ')              /* Note if any nonblank */
  414.             nbflag = TRUE;              /* in right match. */
  415.         }
  416.  
  417.     cp5 = cp3;
  418.  
  419.     if (nbflag == FALSE)                /* Erase to EOL ? */
  420.         {
  421.         while (cp5!=cp1 && cp5[-1]==' ')
  422.             --cp5;
  423.  
  424.         if (cp3-cp5 <= 3)               /* Use only if erase is */
  425.             cp5 = cp3;                  /* fewer characters. */
  426.         }
  427.  
  428.     movecursor(row, cp1-&vline[0]);     /* Go to start of line. */
  429.  
  430.     while (cp1 != cp5)                  /* Ordinary. */
  431.         {
  432.         (*term.t_putchar)(*cp1);
  433.         ++ttcol;
  434.         *cp2++ = *cp1++;
  435.         }
  436.  
  437.     if (cp5 != cp3)                     /* Erase. */
  438.         {
  439.         (*term.t_eeol)();
  440.         while (cp1 != cp3)
  441.             *cp2++ = *cp1++;
  442.         }
  443. #endif
  444. }
  445.  
  446. /*
  447.  * Redisplay the mode line for the window pointed to by the "wp". This is the
  448.  * only routine that has any idea of how the modeline is formatted. You can
  449.  * change the modeline format by hacking at this routine. Called by "update"
  450.  * any time there is a dirty window.
  451.  */
  452. modeline(wp)
  453.     WINDOW *wp;
  454. {
  455.     register char *cp;
  456.     register int c;
  457.     register int n;
  458.     register BUFFER *bp;
  459.  
  460.     n = wp->w_toprow+wp->w_ntrows;              /* Location. */
  461.     vscreen[n]->v_flag |= VFCHG;                /* Redraw next time. */
  462.     vtmove(n, 0);                               /* Seek to right line. */
  463.     vtputc('-');
  464.     bp = wp->w_bufp;
  465.  
  466.     if ((bp->b_flag&BFCHG) != 0)                /* "*" if changed. */
  467.         vtputc('*');
  468.     else
  469.         vtputc('-');
  470.  
  471.     n  = 2;
  472.     cp = " MicroEMACS -- ";                     /* Buffer name. */
  473.  
  474.     while ((c = *cp++) != 0)
  475.         {
  476.         vtputc(c);
  477.         ++n;
  478.         }
  479.  
  480.     cp = &bp->b_bname[0];
  481.  
  482.     while ((c = *cp++) != 0)
  483.         {
  484.         vtputc(c);
  485.         ++n;
  486.         }
  487.  
  488.     vtputc(' ');
  489.     ++n;
  490.  
  491.     if (bp->b_fname[0] != 0)            /* File name. */
  492.         {
  493.         cp = "-- File: ";
  494.  
  495.         while ((c = *cp++) != 0)
  496.             {
  497.             vtputc(c);
  498.             ++n;
  499.             }
  500.  
  501.         cp = &bp->b_fname[0];
  502.  
  503.         while ((c = *cp++) != 0)
  504.             {
  505.             vtputc(c);
  506.             ++n;
  507.             }
  508.  
  509.         vtputc(' ');
  510.         ++n;
  511.         }
  512.  
  513. #if WFDEBUG
  514.     vtputc('-');
  515.     vtputc((wp->w_flag&WFMODE)!=0  ? 'M' : '-');
  516.     vtputc((wp->w_flag&WFHARD)!=0  ? 'H' : '-');
  517.     vtputc((wp->w_flag&WFEDIT)!=0  ? 'E' : '-');
  518.     vtputc((wp->w_flag&WFMOVE)!=0  ? 'V' : '-');
  519.     vtputc((wp->w_flag&WFFORCE)!=0 ? 'F' : '-');
  520.     n += 6;
  521. #endif
  522.  
  523.     while (n < term.t_ncol)             /* Pad to full width. */
  524.         {
  525.         vtputc('-');
  526.         ++n;
  527.         }
  528. }
  529.  
  530. /*
  531.  * Send a command to the terminal to move the hardware cursor to row "row"
  532.  * and column "col". The row and column arguments are origin 0. Optimize out
  533.  * random calls. Update "ttrow" and "ttcol".
  534.  */
  535. movecursor(row, col)
  536.     {
  537.     if (row!=ttrow || col!=ttcol)
  538.         {
  539.         ttrow = row;
  540.         ttcol = col;
  541.         (*term.t_move)(row, col);
  542.         }
  543.     }
  544.  
  545. /*
  546.  * Erase the message line. This is a special routine because the message line
  547.  * is not considered to be part of the virtual screen. It always works
  548.  * immediately; the terminal buffer is flushed via a call to the flusher.
  549.  */
  550. mlerase()
  551.     {
  552.     movecursor(term.t_nrow, 0);
  553.     (*term.t_eeol)();
  554.     (*term.t_flush)();
  555.     mpresf = FALSE;
  556.     }
  557.  
  558. /*
  559.  * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  560.  * ABORT. The ABORT status is returned if the user bumps out of the question
  561.  * with a ^G. Used any time a confirmation is required.
  562.  */
  563. mlyesno(prompt)
  564.     char *prompt;
  565.     {
  566.     register int s;
  567.     char buf[64];
  568.  
  569.     for (;;)
  570.         {
  571.         strcpy(buf, prompt);
  572.         strcat(buf, " [y/n]? ");
  573.         s = mlreply(buf, buf, sizeof(buf));
  574.  
  575.         if (s == ABORT)
  576.             return (ABORT);
  577.  
  578.         if (s != FALSE)
  579.             {
  580.             if (buf[0]=='y' || buf[0]=='Y')
  581.                 return (TRUE);
  582.  
  583.             if (buf[0]=='n' || buf[0]=='N')
  584.                 return (FALSE);
  585.             }
  586.         }
  587.     }
  588.  
  589. /*
  590.  * Write a prompt into the message line, then read back a response. Keep
  591.  * track of the physical position of the cursor. If we are in a keyboard
  592.  * macro throw the prompt away, and return the remembered response. This
  593.  * lets macros run at full speed. The reply is always terminated by a carriage
  594.  * return. Handle erase, kill, and abort keys.
  595.  */
  596. mlreply(prompt, buf, nbuf)
  597.     char *prompt;
  598.     char *buf;
  599.     {
  600.     register int cpos;
  601.     register int i;
  602.     register int c;
  603.  
  604.     cpos = 0;
  605.  
  606.     if (kbdmop != NULL)
  607.         {
  608.         while ((c = *kbdmop++) != '\0')
  609.             buf[cpos++] = c;
  610.  
  611.         buf[cpos] = 0;
  612.  
  613.         if (buf[0] == 0)
  614.             return (FALSE);
  615.  
  616.         return (TRUE);
  617.         }
  618.  
  619.     mlwrite(prompt);
  620.  
  621.     for (;;)
  622.         {
  623.         c = (*term.t_getchar)();
  624.  
  625.         switch (c)
  626.             {
  627.             case 0x0D:                  /* Return, end of line */
  628.                 buf[cpos++] = 0;
  629.  
  630.                 if (kbdmip != NULL)
  631.                     {
  632.                     if (kbdmip+cpos > &kbdm[NKBDM-3])
  633.                         {
  634.                         ctrlg(FALSE, 0);
  635.                         (*term.t_flush)();
  636.                         return (ABORT);
  637.                         }
  638.  
  639.                     for (i=0; i<cpos; ++i)
  640.                         *kbdmip++ = buf[i];
  641.                     }
  642.  
  643.                 (*term.t_putchar)('\r');
  644.                 ttcol = 0;
  645.                 (*term.t_flush)();
  646.  
  647.                 if (buf[0] == 0)
  648.                     return (FALSE);
  649.  
  650.                 return (TRUE);
  651.  
  652.             case 0x07:                  /* Bell, abort */
  653.                 (*term.t_putchar)('^');
  654.                 (*term.t_putchar)('G');
  655.                 ttcol += 2;
  656.                 ctrlg(FALSE, 0);
  657.                 (*term.t_flush)();
  658.                 return (ABORT);
  659.  
  660.             case 0x7F:                  /* Rubout, erase */
  661.             case 0x08:                  /* Backspace, erase */
  662.                 if (cpos != 0)
  663.                     {
  664.                     (*term.t_putchar)('\b');
  665.                     (*term.t_putchar)(' ');
  666.                     (*term.t_putchar)('\b');
  667.                     --ttcol;
  668.  
  669.                     if (buf[--cpos] < 0x20)
  670.                         {
  671.                         (*term.t_putchar)('\b');
  672.                         (*term.t_putchar)(' ');
  673.                         (*term.t_putchar)('\b');
  674.                         --ttcol;
  675.                         }
  676.  
  677.                     (*term.t_flush)();
  678.                     }
  679.  
  680.                 break;
  681.  
  682.             case 0x15:                  /* C-U, kill */
  683.                 while (cpos != 0)
  684.                     {
  685.                     (*term.t_putchar)('\b');
  686.                     (*term.t_putchar)(' ');
  687.                     (*term.t_putchar)('\b');
  688.                     --ttcol;
  689.  
  690.                     if (buf[--cpos] < 0x20)
  691.                         {
  692.                         (*term.t_putchar)('\b');
  693.                         (*term.t_putchar)(' ');
  694.                         (*term.t_putchar)('\b');
  695.                         --ttcol;
  696.                         }
  697.                     }
  698.  
  699.                 (*term.t_flush)();
  700.                 break;
  701.  
  702.             default:
  703.                 if (cpos < nbuf-1)
  704.                     {
  705.                     buf[cpos++] = c;
  706.  
  707.                     if (c < ' ')
  708.                         {
  709.                         (*term.t_putchar)('^');
  710.                         ++ttcol;
  711.                         c ^= 0x40;
  712.                         }
  713.  
  714.                     (*term.t_putchar)(c);
  715.                     ++ttcol;
  716.                     (*term.t_flush)();
  717.                     }
  718.             }
  719.         }
  720.     }
  721.  
  722. /*
  723.  * Write a message into the message line. Keep track of the physical cursor
  724.  * position. A small class of printf like format items is handled. Assumes the
  725.  * stack grows down; this assumption is made by the "++" in the argument scan
  726.  * loop. Set the "message line" flag TRUE.
  727.  */
  728. mlwrite(fmt, arg)
  729.     char *fmt;
  730.     {
  731.     register int c;
  732.     register char *ap;
  733.  
  734.     movecursor(term.t_nrow, 0);
  735.     ap = (char *) &arg;
  736.     while ((c = *fmt++) != 0) {
  737.         if (c != '%') {
  738.             (*term.t_putchar)(c);
  739.             ++ttcol;
  740.             }
  741.         else
  742.             {
  743.             c = *fmt++;
  744.             switch (c) {
  745.                 case 'd':
  746.                     mlputi(*(int *)ap, 10);
  747.                     ap += sizeof(int);
  748.                     break;
  749.  
  750.                 case 'o':
  751.                     mlputi(*(int *)ap,  8);
  752.                     ap += sizeof(int);
  753.                     break;
  754.  
  755.                 case 'x':
  756.                     mlputi(*(int *)ap, 16);
  757.                     ap += sizeof(int);
  758.                     break;
  759.  
  760.                 case 'D':
  761.                     mlputli(*(long *)ap, 10);
  762.                     ap += sizeof(long);
  763.                     break;
  764.  
  765.                 case 's':
  766.                     mlputs(*(char **)ap);
  767.                     ap += sizeof(char *);
  768.                     break;
  769.  
  770.                 default:
  771.                     (*term.t_putchar)(c);
  772.                     ++ttcol;
  773.                 }
  774.             }
  775.         }
  776.     (*term.t_eeol)();
  777.     (*term.t_flush)();
  778.     mpresf = TRUE;
  779.     }
  780.  
  781. /*
  782.  * Write out a string. Update the physical cursor position. This assumes that
  783.  * the characters in the string all have width "1"; if this is not the case
  784.  * things will get screwed up a little.
  785.  */
  786. mlputs(s)
  787.     char *s;
  788.     {
  789.     register int c;
  790.  
  791.     while ((c = *s++) != 0)
  792.         {
  793.         (*term.t_putchar)(c);
  794.         ++ttcol;
  795.         }
  796.     }
  797.  
  798. /*
  799.  * Write out an integer, in the specified radix. Update the physical cursor
  800.  * position. This will not handle any negative numbers; maybe it should.
  801.  */
  802. mlputi(i, r)
  803.     {
  804.     register int q;
  805.     static char hexdigits[] = "0123456789ABCDEF";
  806.  
  807.     if (i < 0)
  808.         {
  809.         i = -i;
  810.         (*term.t_putchar)('-');
  811.         }
  812.  
  813.     q = i/r;
  814.  
  815.     if (q != 0)
  816.         mlputi(q, r);
  817.  
  818.     (*term.t_putchar)(hexdigits[i%r]);
  819.     ++ttcol;
  820.     }
  821.  
  822. /*
  823.  * do the same except as a long integer.
  824.  */
  825. mlputli(l, r)
  826.     long l;
  827.     {
  828.     register long q;
  829.  
  830.     if (l < 0)
  831.         {
  832.         l = -l;
  833.         (*term.t_putchar)('-');
  834.         }
  835.  
  836.     q = l/r;
  837.  
  838.     if (q != 0)
  839.         mlputli(q, r);
  840.  
  841.     (*term.t_putchar)((int)(l%r)+'0');
  842.     ++ttcol;
  843.     }
  844.  
  845. #if RAINBOW
  846.  
  847. putline(row, col, buf)
  848.     int row, col;
  849.     char buf[];
  850.     {
  851.     int n;
  852.  
  853.     n = strlen(buf);
  854.     if (col + n - 1 > term.t_ncol)
  855.         n = term.t_ncol - col + 1;
  856.     Put_Data(row, col, n, buf);
  857.     }
  858. #endif
  859.  
  860.